package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.pojo.product.vo.SpuDetailStandardVO;
import cn.tedu.mall.pojo.product.vo.SpuStandardVO;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuDetailSimpleVO;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuVO;
import cn.tedu.mall.product.service.ISpuService;
import cn.tedu.mall.product.service.seckill.IForSeckillSpuService;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.service.ISeckillSpuService;
import cn.tedu.mall.seckill.utils.RedisBloomUtils;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import com.github.pagehelper.PageInfo;
import com.mchange.v2.beans.BeansUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SeckillSpuServiceImpl implements ISeckillSpuService {
    @Autowired
    private SeckillSpuMapper seckillSpuMapper;
    @DubboReference
    private IForSeckillSpuService dubboSeckillSpuService;
    @Autowired
    private RedisBloomUtils redisBloomUtils;
    @Autowired
    private RedisTemplate redisTemplate;
    public static final String SECKILL_SPU_DETAIL_VO_PREFIX = "seckill:spu:detail:vo:";
    /**
     * 查询秒杀商品列表
     */
    @Override
    public JsonPage<SeckillSpuVO> listSeckillSpus(Integer page, Integer pageSize) {
        List<SeckillSpu> seckillSpus = seckillSpuMapper.findSeckillSpus();
        List<SeckillSpuVO> seckillSpuVOS = new ArrayList<>();
        for (SeckillSpu seckillSpu : seckillSpus) {
            Long spuId = seckillSpu.getSpuId();
            //获取常规spu信息
            SpuStandardVO spu = dubboSeckillSpuService.getSpuById(spuId);
            //实例化,赋值
            SeckillSpuVO seckillSpuVO = new SeckillSpuVO();
            BeanUtils.copyProperties(spu,seckillSpuVO);

            seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());
            //新增到集合
            seckillSpuVOS.add(seckillSpuVO);
        }

        return JsonPage.restPage(new PageInfo<>(seckillSpuVOS));
    }

    @Override
    public SeckillSpuVO getSeckillSpu(Long spuId) {
        //先获取布隆过滤器,判断参数spuId是否在布隆过滤器中,如果不在直接抛出异常
        String bloomTodayKey = SeckillCacheUtils.getBloomFilterKey(LocalDate.now());
        log.info("当前批次的布隆过滤器key为:{}",bloomTodayKey);
        //判断要访问的spuId是否在布隆过滤器中
        if(!redisBloomUtils.bfexists(bloomTodayKey,spuId+"")){
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,"您访问的商品不存在(布隆过滤器判断)");
        }
        //获取要使用的spu对应的常量
        String seckillSpuKey = SeckillCacheUtils.getSeckillSpuVOKey(spuId);
        //声明一个返回值类型的对象
        SeckillSpuVO seckillSpuVO;
        if(redisTemplate.hasKey(seckillSpuKey)){
            seckillSpuVO = (SeckillSpuVO) redisTemplate.boundValueOps(seckillSpuKey).get();
        }else {
            //查询秒杀信息
            SeckillSpu seckillSpu = seckillSpuMapper.findSeckillSpuById(spuId);
            //判断seckillSpu是否为空(因为布隆过滤器可能产生误判)
            if(seckillSpu==null){
                throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,"您访问的商品不存在");
            }
            //查询spu常规信息
            SpuStandardVO standardVO = dubboSeckillSpuService.getSpuById(spuId);

            seckillSpuVO = new SeckillSpuVO();
            BeanUtils.copyProperties(standardVO,seckillSpuVO);

            seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());
            redisTemplate.boundValueOps(seckillSpuKey)
                    .set(seckillSpuVO,10*60*1000+RandomUtils.nextInt(1000),TimeUnit.MILLISECONDS);
        }
        //给seckillSpuVO的url赋值,一旦给url赋值,就意味着当前用户可以提交购买订单
        LocalDateTime nowTime = LocalDateTime.now();
        // 当前是高并发状态,不要链接数据库判断
        //使用Duration来判断时间关系,如果时间差是负数,会返回一个negative的状态
        Duration afterTime = Duration.between(nowTime,seckillSpuVO.getStartTime());
        Duration beforeTime = Duration.between(seckillSpuVO.getEndTime(),nowTime);
        //判断两个变量是否为negative,
        if(afterTime.isNegative() && beforeTime.isNegative()){
           String randCodeKey = SeckillCacheUtils.getRandCodeKey(spuId);
           String randCode = redisTemplate.boundValueOps(randCodeKey).get().toString();

           seckillSpuVO.setUrl("/seckill/"+randCode);
           log.info("url随机码为:{}",randCode);
        }

        return seckillSpuVO;
    }

    @Override
    public SeckillSpuDetailSimpleVO getSeckillSpuDetail(Long spuId) {
        //获得常量Key
        String seckillSpuDetailKey=SECKILL_SPU_DETAIL_VO_PREFIX+spuId;
        //先声明一个当前方法的返回值类型的对象
        SeckillSpuDetailSimpleVO simpleVO;
        //判断redis中是否已经包含这个key
        if(redisTemplate.hasKey(seckillSpuDetailKey)){
            //如果已经保存在Redis中,直接赋值
            simpleVO = (SeckillSpuDetailSimpleVO)redisTemplate.boundValueOps(seckillSpuDetailKey).get();

        }else {
            //如果没呀这个对象,则向数据库查询
            SpuDetailStandardVO spuDetailStandardVO = dubboSeckillSpuService.getSpuDetailById(spuId);
            simpleVO = new SeckillSpuDetailSimpleVO();
            BeanUtils.copyProperties(spuDetailStandardVO,simpleVO);
            //将simpleVO保存到redis中
            redisTemplate.boundValueOps(seckillSpuDetailKey)
                    .set(simpleVO,10*60*1000+ RandomUtils.nextInt(10000), TimeUnit.MILLISECONDS);
        }
        return simpleVO;
    }

















}
